--[[
	Holds all results for the "current" scan of the auction house.
	
	Copyright (C) Udorn (Blackhand)
	
	This program is free software; you can redistribute it and/or
	modify it under the terms of the GNU General Public License
	as published by the Free Software Foundation; either version 2
	of the License, or (at your option) any later version.

	This program is distributed in the hope that it will be useful,
	but WITHOUT ANY WARRANTY; without even the implied warranty of
	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
	GNU General Public License for more details.

	You should have received a copy of the GNU General Public License
	along with this program; if not, write to the Free Software
	Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.	
--]]

vendor.ScanSnapshot = {}
vendor.ScanSnapshot.prototype = {}
vendor.ScanSnapshot.metatable = {__index = vendor.ScanSnapshot.prototype}

--[[
	Compares the two packed entries (or plain keys) and returns -1, if
	the first parameter is smaller as the second, 1 if the second is greater
	and 0 if both are equal.
--]]
local function _KeyCompare(a, b)
	local _, _, key = string.find(a, "^([0-9:]+);");
	local keyA = key or a;
	local _, _, key = string.find(b, "^([0-9:]+);");
	local keyB = key or b;
	if (keyA < keyB) then
		return -1;
	elseif (keyA == keyB) then
		return 0;
	end
	return 1;		
end

--[[
	Compares all aspects of the two packed entries and returns -1, if
	the first parameter is smaller as the second, 0 if they are equal
	and otherwise 1.
--]]
local function _CompareNum(a, b)
	--local aKey, _ = strsplit("#", a);
	--local bKey, _ = strsplit("#", b);
	local aKey = vendor.ScanResults.ExtractKey(a);
	local bKey = vendor.ScanResults.ExtractKey(b);
	if (aKey < bKey) then
		return -1;
	elseif (aKey == bKey) then
		return 0;
	end
	return 1;
end

--[[
	Compares all key aspects of the two packed entries and returns true, if
	the first parameter is smaller as the second.
--]]
local function _Compare(a, b)
	--local aKey, _ = strsplit("#", a);
	--local bKey, _ = strsplit("#", b);	
	local aKey = vendor.ScanResults.ExtractKey(a);
	local bKey = vendor.ScanResults.ExtractKey(b);
	return aKey < bKey;
end

--[[
	Checks whether the list has to be sorted and does it in case.
--]]
local function _EnsureSorting(self)
	if (not self.data.sorted) then
		table.sort(self.index, _Compare);
	end
	self.data.sorted = true;
end

--[[
	Searches the given key in a binary fashion. The function
	ensures that the input data is sorted.
	@param data the data struct of a result object.
	@param key the item link key to be searched.
	@return the index of an item found or nil.
--]]
local function _BinarySearch(self, key)
	local rtn = nil;
	_EnsureSorting(self);
	local n = table.getn(self.index);
	--MyTricksInfo("_BinarySearch n="..n);
	local low = 1
	local high = n + 1;
	while (low < high) do
		local mid = math.floor((low + high) / 2);
		local key2 = vendor.ScanResults.ExtractKey(self.index[mid]);
		local cmp = _KeyCompare(key2 , key); 
		if (cmp == 0) then
			rtn = mid;
			break;
		elseif (cmp < 0) then
			low = mid + 1; 
		else
			high = mid; 
	   end
	end
	return rtn;  	
end

--[[
	Returns the first index for the given key. Wil return nil, if not found.
--]]
local function _GetFirstIndex(self, key)
	local rtn = _BinarySearch(self, key);
	--MyTricksInfo("_GetFirstIndex rtn="..rtn);
	if (rtn) then
		-- search to the left
		while (rtn > 1) do
			local k = vendor.ScanResults.ExtractKey(self.index[rtn - 1]);
			if (k == key) then
				rtn = rtn - 1;
			else
				break;
			end
		end
		
	end
	return rtn;
end


--[[ 
	Creates an instance using the given data container. It should
	be managed in a AceDB.
--]]
function vendor.ScanSnapshot:new(data)
	local instance = setmetatable({}, self.metatable)
	data = data or {}
	if (not data.index) then
		data.index = {}
	end
	instance.data = data
	instance.index = data.index 
	return instance
end

--[[
	Adds a new scan result with the item link, the time left (GetAuctionItemTimeLeft) and some parameters
	found in GetAuctionItemInfo.
--]]
function vendor.ScanSnapshot.prototype:AddScanResult(itemLink, timeLeft, count, minBid, minIncrement, buyoutPrice, bidAmount, owner)
	if (itemLink) then
		assert(owner);
		local key = vendor.Items:GetItemLinkKey(itemLink);
		table.insert(self.index, vendor.ScanResults.Pack(itemLink, time(), timeLeft, count, minBid, minIncrement, buyoutPrice or 0, bidAmount, owner));
		self.data.sorted = false;
	end
end

--[[
	Returns the total number of entries.
--]]
function vendor.ScanSnapshot.prototype:Size()
	return table.getn(self.index);
end

--[[ 
	Returns the n'th entry starting from 1.
	@return itemLinkKey, time, timeLeft, count, minBid, minIncrement, buyoutPrice, bidAmount, owner
--]]
function vendor.ScanSnapshot.prototype:Get(n)
	_EnsureSorting(self);
	local data = self.index[n];
	return vendor.ScanResults.Unpack(data);
end

--[[
	Returns a ScanSet with the specified item.
--]]
function vendor.ScanSnapshot.prototype:GetScanSet(isNeutral, itemLink)
	local index = {};
	local key = vendor.Items:GetItemLinkKey(itemLink);
	local idx = _GetFirstIndex(self, key);
	--MyTricksInfo("vendor.ScanSnapshot:GetScanSet() idx="..idx);
	if (idx) then
		local n = table.getn(self.index);
		while (idx <= n) do
			local data = self.index[idx];
			local k = vendor.ScanResults.ExtractKey(data);
			if (k == key) then
				table.insert(index, data);
				idx = idx + 1;
			else
				break;
			end
		end
	end
	return vendor.ScanSet:new(index, isNeutral, itemLink);
end

function vendor.ScanSnapshot.prototype:GetScanSetBy1stIndex(idx)
	local index = {};
	local sample=self.index[idx];
	if not sample then
		return nil
	end 
	--MyTricksInfo("vendor.ScanSnapshot.prototype:GetScanSetBy1stIndex() idx="..idx);
	local key = vendor.ScanResults.ExtractKey(sample);
	if not key then
		return nil
	end
	
	local itemLink=vendor.ScanResults.ExtractItemLink(sample);
	--MyTricksInfo("vendor.ScanSnapshot.prototype:GetScanSetBy1stIndex() itemId="..itemId);
	if (idx) then
		local n = table.getn(self.index);
		if(idx > n or idx <= 0) then
			return nil;
		end
		while (idx <= n) do
			local data = self.index[idx];
			local k = vendor.ScanResults.ExtractKey(data);
			if (k == key) then
				table.insert(index, data);
				idx = idx + 1;
			else
				break;
			end
		end
	end
	return vendor.ScanSet:new(index, false, itemLink);
end


--[[
	Deletes all results of the given item.
--]]
function vendor.ScanSnapshot.prototype:DeleteResults(itemLink)
	if (itemLink) then
		local key = vendor.Items:GetItemLinkKey(itemLink);
		local idx = _GetFirstIndex(self, key);
		if (idx) then
			while (idx <= table.getn(self.index)) do
				local data = self.index[idx];
				local k = vendor.ScanResults.ExtractKey(data);
				if (k == key) then
					table.remove(self.index, idx);
				else
					break;
				end
			end
		end		
	end
end

--[[
	Inserts all results of the given snapshot.
--]]
function vendor.ScanSnapshot.prototype:InsertResults(snapshot)
	for i=1, table.getn(snapshot.index) do
		table.insert(self.index, snapshot.index[i]);
	end
	self.data.sorted = false;
end

--[[
	Returns the underlying data struct for replacing the one in the AceDB.
--]]
function vendor.ScanSnapshot.prototype:GetData()
	return self.data;
end
